﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Web;
using System.Web.Services;
using System.Xml.XPath;
using System.Net;

using Newtonsoft.Json;

namespace GetWebInfo
{

    /*
     * Base class of Financial Reports
     */ 
    public class Statement
    {
        public Statement()
        {
        }

        /// <summary>
        /// 分析网页内容，并保存 Finance或 Industrial两种类型的报表
        /// </summary>
        /// <param name="strPageInfo"></param>
        /// <returns></returns>
        public static void Parse(string stockCode, bool hasFirstQuarter, bool hasHalfYear, bool hasThreeQuarter, bool hasYear, string startYear, string endYear, bool isAll)
        {
            /*
            * 1，获取股票代码，并建立文件夹
             * 2，如果是获取全部，则进入isAll，获取所有的accountdate
             * 3，否则进入指定年份
             * 3.1，选择年份范围
             * 3.2，获取所有的accountdate
            * 4，循环路径，组织成三个网址（就三个报表）
            * 5，获取原先下载的内容
            * 6，送入分析网页
            * 7，存储成json
            */

            if (stockCode.Length != 6)
            {
                Console.WriteLine("The stockCode is empty!");
                return;
            }

            int _startYear = 0;
            int _endYear = 0;

            WebClient _myWebClient = new WebClient();
            string _urlPath = string.Empty;
            List<string> _isAllPageDates = new List<string>();

            if (isAll)
            {
                _urlPath = string.Format(Const.PagePath_HexunStatement_all, stockCode);
                if (Const.OnlyTest) _urlPath = @"C:\zcfz";
                string _str = Encoding.Default.GetString(_myWebClient.DownloadData(_urlPath));
                _isAllPageDates = GetPathes(stockCode, _str);
            }
            else
            {
                if (!hasFirstQuarter && !hasHalfYear && !hasThreeQuarter && !hasYear)
                {
                    Console.WriteLine("必须选择一个季度的！");
                    return;
                }

                if (startYear.Length != 4 || endYear.Length != 4)
                {
                    Console.WriteLine("输入的年份不正确！");
                    return;
                }
                _startYear = int.Parse(startYear);
                _endYear = int.Parse(endYear);

                int _year = _startYear;
                for (int i = _startYear; i <= _endYear; i++)
                {
                    if (hasFirstQuarter)
                    {
                        _isAllPageDates.Add(string.Format("{0}.03.15", _year));
                    }
                    if (hasHalfYear)
                    {
                        _isAllPageDates.Add(string.Format("{0}.06.30", _year));
                    }
                    if (hasThreeQuarter)
                    {
                        _isAllPageDates.Add(string.Format("{0}.09.30", _year));
                    }
                    if (hasYear)
                    {
                        _isAllPageDates.Add(string.Format("{0}.12.31", _year));
                    }
                    _year++;
                }
            }
               
            string _directoryPath = string.Format(@"{0}\{1}", System.AppDomain.CurrentDomain.BaseDirectory, stockCode);
            if (!Directory.Exists(_directoryPath))
            {
                Directory.CreateDirectory(_directoryPath);
            }

            Console.WriteLine("Start get {0}'s statements.", stockCode);

            /*
             * 1，选择了谁，就尝试获取谁；
             * 2，一季度的存成：20111.json.temp，二季度的：20112.json.temp，三季度的：20113.json.temp。每个季度都是累加的。
             * 3，全年的存成：2011.json                 * 
             * 4，通过按钮将每个季度规整为单独的，例如20111.json 20112.json 20113.json 20114.json
             */

            //4
            foreach (string _thePath in _isAllPageDates)
            {
                Console.WriteLine("The Page's accountdate is {0}.", _thePath);

                string _myWebInfo1 = string.Empty;
                string _myWebInfo2 = string.Empty;
                string _myWebInfo3 = string.Empty;

                //5
                _urlPath = string.Format(Const.PagePath_HexunStatement_zcfz, stockCode, _thePath);

                if (Const.OnlyTest) _urlPath = @"H:\forge\自动下载程序的需求\exe\600016-test\zcfz";
                _myWebInfo1 = Encoding.Default.GetString(_myWebClient.DownloadData(_urlPath));

                _urlPath = string.Format(Const.PagePath_HexunStatement_lr, stockCode, _thePath);

                if (Const.OnlyTest) _urlPath = @"H:\forge\自动下载程序的需求\exe\600016-test\lr";
                _myWebInfo2 = Encoding.Default.GetString(_myWebClient.DownloadData(_urlPath));

                _urlPath = string.Format(Const.PagePath_HexunStatement_xjll, stockCode, _thePath);

                if (Const.OnlyTest) _urlPath = @"H:\forge\自动下载程序的需求\exe\600016-test\xjll";
                _myWebInfo3 = Encoding.Default.GetString(_myWebClient.DownloadData(_urlPath));
                
                //6
                ParsePageInfo(stockCode, GetFileName(_thePath), _myWebInfo1, _myWebInfo2, _myWebInfo3);
            }
        }

        

        /// <summary>
        /// 整理Temp的报表
        /// </summary>
        /// <param name="stockCode"></param>
        public static void RePackTempToJson(string stockCode)
        {

            /*
             * 由于和讯的损益表与现金流量表是累加的，因此应分别计算2、3、4季度的，1季度的改名即可
             * 先从四季度的开始计算：
             * 2011.json        全年的
             * 20114.json       = 2011.json         -   20113.json.temp
             * 20113.json       = 20113.json.temp   -   20112.json.temp
             * 20112.json       = 20112.json.temp   -   20111.json.temp
             * 20111.json       = 20111.json.temp
             * 
             * 1，获取所有文件名；
             * 2，看是否有包含temp，没有返回；
             * 3，找有temp的年，送入分析函数；
             * 4，删除所有temp
             */

            string _directoryPath = string.Format(@"{0}\{1}\",
                System.AppDomain.CurrentDomain.BaseDirectory,
                stockCode);

            DirectoryInfo _diri = new DirectoryInfo(_directoryPath);
            FileInfo[] _fis = _diri.GetFiles("*.temp");
            if (_fis.Length == 0)
            {
                Console.WriteLine("can't find *.temp");
                return;
            }

            foreach (FileInfo _fi in _fis)
            {
                RePackStatements(stockCode, _fi.Name);
                _fi.Delete();
            }

        }        

        /// <summary>
        /// 装载现有报表
        /// </summary>
        /// <param name="datetime"></param>
        /// <param name="stockCode"></param>
        /// <returns></returns>
        public static Statement Load(string datetime, string stockCode)
        {
            string _filePath = string.Format(@"{0}\{1}\{2}{3}",
                System.AppDomain.CurrentDomain.BaseDirectory,
                stockCode,
                datetime,
                Const.ReportExtName);

            if (!File.Exists(_filePath))
            {
                return null;
            }
            
            return Load(_filePath);;
        }

        public static Statement Load(string strFileFullName)
        {
            if (!File.Exists(strFileFullName))
            {
                return null;
            }

            StreamReader _sr = new StreamReader(strFileFullName);
            string _fileInfo = _sr.ReadToEnd();
            _sr.Close();

            Statement _fs = null;
            if (_fileInfo.Contains("Cash_and_Balances_with_Central_Bank"))
            {
                _fs = JsonConvert.DeserializeObject<FinanceStatements>(_fileInfo);
            }
            else
            {
                _fs = JsonConvert.DeserializeObject<IndustrialStatements>(_fileInfo);
            }

            return _fs;
        }

        /// <summary>
        /// 保存报表
        /// </summary>
        /// <param name="datetime"></param>
        /// <param name="stockCode"></param>
        public void Save(string datetime, string stockCode)
        {
            string _extName = Const.ReportExtName;
            if (datetime.Length == 5)
            {
                _extName = Const.ReportTempExtName;
            }
            string _filePath = string.Format(@"{0}\{1}\{2}{3}",
                System.AppDomain.CurrentDomain.BaseDirectory,
                stockCode,
                datetime,
                _extName);
            this.Save(_filePath);
        }

        public void Save(string strFileFullName)
        {
            string _output = JsonConvert.SerializeObject(this, Formatting.Indented);
            StreamWriter _sw = new StreamWriter(strFileFullName);
            _sw.Write(_output);
            _sw.Close();
        }

        /// <summary>
        /// 报表类别
        /// </summary>
        [StatementsAttribute("报表类别StatementClassification", StatementType.Other)]
        public StatementClassification StatementClass = StatementClassification.Industrial;


        private static void RePackStatements(string stockCode, string datetime)
        {
            datetime = datetime.Substring(0, 4);
            GetStatements(stockCode, "4", datetime);
            GetStatements(stockCode, "3", datetime);
            GetStatements(stockCode, "2", datetime);

            string _filePath = string.Format(@"{0}\{1}\{2}", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, string.Format("{0}1.json.temp", datetime));
            if (File.Exists(_filePath))
            {
                File.Move(_filePath, _filePath.Replace(".temp", ""));
            }
        }

        private static void GetStatements(string stockCode, string str4321, string datetime)
        {
            /*
             *  1，如果没有20114.json，如果有2011.json与20113.json.temp，计算20114.json的
             *  2，如果没有20113.json，如果有20113.json.temp与20112.json.temp，计算20113.json的
             *  2，如果没有20112.json，如果有20112.json.temp与20111.json.temp，计算20112.json的
             *  3，如果有20111.json.temp，改名为20111.json
             */
            Statement _fs1;
            Statement _fs2;
            Statement _fs3;
            Statement _fs4;
            Statement _fs;

            //1
            string _fsstr = string.Empty;
            string _fsstr1 = string.Empty;
            string _fsstr2 = string.Empty;
            string _fsstr3 = string.Empty;
            string _fsstr4 = string.Empty;

            if (str4321 == "4")
            {
                _fsstr = string.Format(@"{0}\{1}\{2}4.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
            }
            else if (str4321 == "3")
            {
                _fsstr = string.Format(@"{0}\{1}\{2}3.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
            }
            else if (str4321 == "2")
            {
                _fsstr = string.Format(@"{0}\{1}\{2}2.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
            }

            if (!File.Exists(_fsstr))
            {
                if (str4321 == "4")
                {
                    _fsstr1 = string.Format(@"{0}\{1}\{2}.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    _fsstr2 = string.Format(@"{0}\{1}\{2}3.json.temp", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    if (!File.Exists(_fsstr2))
                    {
                        _fsstr2 = string.Format(@"{0}\{1}\{2}3.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                        _fsstr3 = string.Format(@"{0}\{1}\{2}2.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                        _fsstr4 = string.Format(@"{0}\{1}\{2}1.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    }

                    if (File.Exists(_fsstr1) && File.Exists(_fsstr2) && File.Exists(_fsstr3) && File.Exists(_fsstr4))
                    {
                        _fs1 = Statement.Load(_fsstr1);
                        _fs2 = Statement.Load(_fsstr2);
                        _fs3 = Statement.Load(_fsstr3);
                        _fs4 = Statement.Load(_fsstr4);

                        if (_fs1 is IndustrialStatements)
                        {
                            _fs = (IndustrialStatements)_fs1 - (IndustrialStatements)_fs2;
                            _fs = (IndustrialStatements)_fs - (IndustrialStatements)_fs3;
                            _fs = (IndustrialStatements)_fs - (IndustrialStatements)_fs4;
                        }
                        else
                        {
                            _fs = (FinanceStatements)_fs1 - (FinanceStatements)_fs2;
                            _fs = (FinanceStatements)_fs - (FinanceStatements)_fs3;
                            _fs = (FinanceStatements)_fs - (FinanceStatements)_fs4;
                        }
                        _fs.Save(_fsstr);
                    }
                }
                else if (str4321 == "3")
                {
                    _fsstr1 = string.Format(@"{0}\{1}\{2}3.json.temp", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    _fsstr2 = string.Format(@"{0}\{1}\{2}2.json.temp", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    if (!File.Exists(_fsstr2))
                    {
                        _fsstr2 = string.Format(@"{0}\{1}\{2}2.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                        _fsstr3 = string.Format(@"{0}\{1}\{2}1.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    }

                    if (File.Exists(_fsstr1) && File.Exists(_fsstr2) && File.Exists(_fsstr3))
                    {
                        _fs1 = Statement.Load(_fsstr1);
                        _fs2 = Statement.Load(_fsstr2);
                        _fs3 = Statement.Load(_fsstr3);

                        if (_fs1 is IndustrialStatements)
                        {
                            _fs = (IndustrialStatements)_fs1 - (IndustrialStatements)_fs2;
                            _fs = (IndustrialStatements)_fs - (IndustrialStatements)_fs3;
                        }
                        else
                        {
                            _fs = (FinanceStatements)_fs1 - (FinanceStatements)_fs2;
                            _fs = (FinanceStatements)_fs - (FinanceStatements)_fs3;
                        }
                        _fs.Save(_fsstr);
                    }
                }
                else if (str4321 == "2")
                {
                    _fsstr1 = string.Format(@"{0}\{1}\{2}2.json.temp", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    _fsstr2 = string.Format(@"{0}\{1}\{2}1.json.temp", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    if (!File.Exists(_fsstr2))
                    {
                        _fsstr2 = string.Format(@"{0}\{1}\{2}1.json", System.AppDomain.CurrentDomain.BaseDirectory, stockCode, datetime);
                    }

                    if (File.Exists(_fsstr1) && File.Exists(_fsstr2))
                    {
                        _fs1 = Statement.Load(_fsstr1);
                        _fs2 = Statement.Load(_fsstr2);

                        if (_fs1 is IndustrialStatements)
                        {
                            _fs = (IndustrialStatements)_fs1 - (IndustrialStatements)_fs2;
                        }
                        else
                        {
                            _fs = (FinanceStatements)_fs1 - (FinanceStatements)_fs2;
                        }
                        _fs.Save(_fsstr);
                    }
                }
            }
        }

        private static string GetFileName(string name)
        {
            /*
             2011.03.15
             2011.06.30
             2011.09.30
             2011.12.31
             */
            string s = name.Substring(6, 1);
            if (s == "3")
            {
                return name.Substring(0, 4) + "1";
            }
            else if (s == "6")
            {
                return name.Substring(0, 4) + "2";
            }
            else if (s == "9")
            {
                return name.Substring(0, 4) + "3";
            }
            return name.Substring(0, 4);
        }

        private static List<string> GetPathes(string stockCode, string _str)
        {
            List<string> _ls = new List<string>();

            string[] strs = _str.Split(new string[] { "				<script type=\"text/javascript\">dateurl=\"" }, StringSplitOptions.RemoveEmptyEntries);
            strs = strs[1].Split(new string[] { ";</script>" }, StringSplitOptions.RemoveEmptyEntries);
            Dictionary<string, string> strpairs = new Dictionary<string, string>();
            strpairs.Add("http://stockdata.stock.hexun.com/2008/zcfz.aspx?stockid=", "");
            strpairs.Add("&accountdate=\"; dateArr = ", "");
            strpairs.Add("[", "");
            strpairs.Add("]", "");
            strpairs.Add("'", "");
            strpairs.Add(stockCode, "");
            _str = Global.StringReplaces(strs[0], strpairs);
            strs = _str.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < strs.Length; i++)
            {
                if (i % 2 != 0)
                {
                    continue;
                }
                _ls.Add(strs[i]);
            }

            return _ls;
        }

        private static void ParsePageInfo(string stockCode, string datetime, string strPageInfo1, string strPageInfo2, string strPageInfo3)
        {
            Statement _sm = Statement.Load(datetime, stockCode);
            List<string> _sourceStrs = new List<string>();
            AddSourceStrs(strPageInfo1, _sourceStrs);
            AddSourceStrs(strPageInfo2, _sourceStrs);
            AddSourceStrs(strPageInfo3, _sourceStrs);

            Type myType;

            if (strPageInfo1.Contains("现金及存放同业款项") ||
                strPageInfo2.Contains("现金及存放同业款项") ||
                strPageInfo3.Contains("现金及存放同业款项"))
            {
                myType = typeof(FinanceStatements);
                if (_sm == null)
                {
                    _sm = new FinanceStatements();
                }
            }
            else
            {
                myType = typeof(IndustrialStatements);
                if (_sm == null)
                {
                    _sm = new IndustrialStatements();
                }
            }
            Console.WriteLine("The statements is {0}.", myType.FullName);

            FieldInfo[] myFields = myType.GetFields(BindingFlags.Public | BindingFlags.Instance);

            for (int i = 0; i < myFields.Length; i++)
            {
                StatementsAttribute obsAttr = (StatementsAttribute)Attribute.GetCustomAttribute(myFields[i], typeof(StatementsAttribute));

                foreach (string ts in _sourceStrs)
                {
                    if (ts.StartsWith(obsAttr.CHSName) &&
                        obsAttr.Type != StatementType.Other)
                    {
                        string sts = ts.Replace(obsAttr.CHSName, "");
                        if (!Global.IsValueFormat(sts))
                        {
                            break;
                        }

                        if (sts == "" || sts == "--")
                        {
                            myFields[i].SetValue(_sm, (decimal)0);
                            Console.WriteLine("key: [{0}] value: [{1}]", obsAttr.CHSName, 0);
                            break;
                        }
                        else
                        {
                            //sts = sts.Substring(0, sts.Length - (sts.Length - sts.IndexOf(".")));
                            myFields[i].SetValue(_sm, decimal.Parse(sts));
                            Console.WriteLine("key: [{0}] value: [{1}]", obsAttr.CHSName, sts);
                            break;
                        }
                    }
                }
            }

            _sm.Save(datetime, stockCode);
        }

        private static void AddSourceStrs(string pageInfo, List<string> sourceStrs)
        {
            //找class="web2">\n，后的一行
            string[] strs = pageInfo.Split(new string[] { "class=\"web2\">" }, StringSplitOptions.RemoveEmptyEntries);
            //string sourceInfo = strs[1].Substring(0, strs[1].Length - (strs[1].Length - strs[1].IndexOf("</tbody></table>")));

            string sourceInfo = strs[1].TrimStart(new char[] { '\t', '\n', '\r' });
            sourceInfo = sourceInfo.Split(new char[] { '\n' })[0];

            //所有页面去废物
            Dictionary<string, string> strpairs = new Dictionary<string, string>();
            strpairs.Add("<span id=\"ControlEx1_lbl\">", "");
            strpairs.Add("</span>", "");
            strpairs.Add("<tbody>", "");
            //strpairs.Add("<tr>", "");
            strpairs.Add("\t", "");
            strpairs.Add("\n", "");
            strpairs.Add("<div class=\'tishi\'>", "");
            strpairs.Add("</div>", "");
            strpairs.Add("<strong>", "");
            strpairs.Add("</strong>", "");
            strpairs.Add("<td class=\'dotborder\' width=\'45%\'>", "");
            strpairs.Add("</td>", "");

            //strpairs.Add("<td clas", "");
            strpairs.Add("<td>", "");
            strpairs.Add(",", "");
            strpairs.Add("<td colspan=\"2\" class=\"lastbgcolor\">", "");
            strpairs.Add("<td height=\"1\" colspan=\"2\" bgcolor=\"#cfcfcf\">", "");
            strpairs.Add("一、", "");
            strpairs.Add("二、", "");
            strpairs.Add("三、", "");
            strpairs.Add("四、", "");
            strpairs.Add("五、", "");
            strpairs.Add("其中：", "");
            strpairs.Add("减：", "");
            strpairs.Add("加：", "");
            strpairs.Add("（一）", "");
            strpairs.Add("（二）", "");

            //strpairs.Add(" ", "");
            sourceInfo = Global.StringReplaces(sourceInfo, strpairs);

            if (sourceStrs == null)
            {
                sourceStrs = new List<string>();
            }
            sourceStrs.AddRange(sourceInfo.Split(new string[] { "<tr>" }, StringSplitOptions.RemoveEmptyEntries));
        }
    }

    /// <summary>
    /// 报表元素的属性，包括：名称、类型
    /// </summary>
    [AttributeUsage(AttributeTargets.All)]
    public class StatementsAttribute : Attribute
    {
        private string _CHSName = "";
        public string CHSName
        {
            get { return this._CHSName; }
        }

        private StatementType _type = StatementType.Balance;
        public StatementType Type
        {
            get { return this._type; }
        }

        public StatementsAttribute(string name, StatementType type)
        {
            this._CHSName = name;
            this._type = type;
        }
    }

    /// <summary>
    /// 三大报表类型：资产负债表、损益表、现金流量表、其它
    /// </summary>
    public enum StatementType : byte
    {
        Balance = 0,
        Operation,
        CashFolw,
        Other
    }

    /// <summary>
    /// 两类报表：工业、金融
    /// </summary>
    public enum StatementClassification : byte
    {
        Industrial = 0,
        Finance
    }

    public enum StatementQuarter : byte
    {
        FirstQuarter = 1,
        HalfYear,
        ThreeQuarter,
        Year
    }
}
